package com.aiti.lulian.workflow;

import java.util.Map;



import com.aiti.base.core.utils.message.Message;
import com.aiti.lulian.entity.ShowRecordAudit;
import com.aiti.lulian.feign.CloudShowManagerSubService;
import com.aiti.lulian.mapper.newCard.CardPersonalBaseInfoMapper;
import com.aiti.lulian.service.ShowRecordAuditService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class WorkflowEngine {

    @Autowired
    private ProcessDefinitionManager processDefinitionManager;

    @Autowired
    private ProcessInstanceManager processInstanceManager;

    @Autowired
    private TaskManager taskManager;

    @Autowired
    private TaskHistoryService taskHistoryService;

    @Autowired
    private CardPersonalBaseInfoMapper baseInfoMapper;

    @Autowired
    private ShowRecordAuditService showRecordAuditService;
    @Autowired
    private CloudShowManagerSubService cloudShowManagerSubService;

    @Transactional(rollbackFor = Exception.class)
    public ProcessInstance startProcess(String processDefinitionId, String businessKey,
                                        Map<String, Object> variables, String starterUserId,String businessType) {

        ProcessInstance instance = processInstanceManager.createInstance(
                processDefinitionId, businessKey, starterUserId, variables);



        executeStartNode(instance, starterUserId, businessType);

        return instance;
    }


    private void executeStartNode(ProcessInstance instance, String actorUserId, String businessType) {
        ProcessDefinition definition = instance.getProcessDefinition();
        Node startNode = definition.getStartNode();

        if (startNode != null) {

            instance.setCurrentNodeId(startNode.getId());
            processInstanceManager.update(instance);


            executeNextNode(instance, null, instance.getVariables(), actorUserId);
            saveAudit(instance, actorUserId, businessType);
        }
    }

    private void saveAudit(ProcessInstance instance, String actorUserId, String businessType) {
        ShowRecordAudit audit = new ShowRecordAudit();
        audit.setCreator(actorUserId);
        audit.setBusinessRecordId(instance.getBusinessKey());
        audit.setProcessInstanceId(instance.getId());
        audit.setBusinessType(businessType);
        Message message = cloudShowManagerSubService.getUserInfo(actorUserId);
        if (message == null) {
            throw new RuntimeException("获取用户信息失败！");
        }
        Object obj = message.getObj();
        if (obj == null) {
            throw new RuntimeException("获取用户信息失败！" );
        }
        if (message.getCode() == 1) {
            throw new RuntimeException("获取用户信息失败");
        }
        ObjectMapper mapper = new ObjectMapper();
        Map resultMap = mapper.convertValue(obj, Map.class);
        String deptId = (String)resultMap.get("deptId");
        String realname = (String)resultMap.get("realname");
        Message userRoleMessage = cloudShowManagerSubService.getRoleByUserId(actorUserId);
        if (userRoleMessage == null) {
            throw new RuntimeException("获取角色信息失败！");
        }
        String roleIds = (String)userRoleMessage.getObj();
        audit.setRoleId(roleIds);
        audit.setDeptId(deptId);
        audit.setCreatorName(realname);
        audit.setBaseCreateTime(new Date());
        showRecordAuditService.save(audit);
    }



    @Transactional
    public void completeTask(String taskId, Map<String, Object> variables,
                             String comment, String actorUserId) {

        taskManager.completeTask(taskId, variables, comment, actorUserId);


        TaskInstance taskInstance = taskManager.findById(taskId);
        if (taskInstance != null) {
            ProcessInstance instance = taskInstance.getProcessInstance();
            ProcessDefinition definition = instance.getProcessDefinition();
            if(definition == null){
                definition = processDefinitionManager.findById(instance.getProcessDefinitionId());
                instance.setProcessDefinition(definition);
            }

            executeNextNode(instance, taskInstance.getTaskDefinitionId(), variables, actorUserId);
        }
    }

    private void executeNextNode(ProcessInstance instance, String completedTaskId,
                                 Map<String, Object> variables, String actorUserId) {
        ProcessDefinition definition = instance.getProcessDefinition();


        String nextNodeId = findNextNodeId(definition, completedTaskId);

        if (nextNodeId == null) {

            instance.complete();
            processInstanceManager.update(instance);
            return;
        }

        Node nextNode = definition.getNode(nextNodeId);
        if (nextNode == null) {
            throw new RuntimeException("未获取到下个审批节点: " + nextNodeId);
        }
        if(!StringUtils.isEmpty(completedTaskId) && !"submit".equals(completedTaskId)){
            instance.setStatus(com.aiti.lulian.workflow.ProcessStatus.RUNNING);
        }

        instance.setCurrentNodeId(nextNodeId);
        processInstanceManager.update(instance);


        executeNode(instance, nextNode, variables, actorUserId);
    }

    private void executeNode(ProcessInstance instance, Node node,
                             Map<String, Object> variables, String actorUserId) {
        if ("userTask".equals(node.getType())) {
            String assigneeId = resolveAssignee(node, variables, actorUserId);
            taskManager.createTask(instance, node, assigneeId, variables);
        } else if ("endEvent".equals(node.getType())) {
            instance.complete();
            processInstanceManager.update(instance);
        }

    }

    private String resolveAssignee(Node node, Map<String, Object> variables, String actorUserId) {
        if ("USER".equals(node.getAssigneeType())) {
            if (node.getAssigneeId().contains("#{")) {
                String expression = node.getAssigneeId().substring(node.getAssigneeId().indexOf("#{") + 2, node.getAssigneeId().indexOf("}"));
                return (String)variables.get(expression);
            }
            return node.getAssigneeId();
        } else if ("ROLE".equals(node.getAssigneeType())) {
            String assigneeId = node.getAssigneeId();
            if (node.getAssigneeId().contains("#{")) {
                String expression = node.getAssigneeId().substring(node.getAssigneeId().indexOf("#{") + 2, node.getAssigneeId().indexOf("}"));
                assigneeId = (String)variables.get(expression);
            }


//            List<String> userIds = baseInfoMapper.selectUserIdListByRoleId(node.getAssigneeId());
//
//
//            if (!userIds.isEmpty()) {
//                return userIds.get(0);
//            }

            throw new RuntimeException("该用户没找到角色: " + node.getAssigneeId());
        }

        throw new RuntimeException("分配类型未找到: " + node.getAssigneeType());
    }



    private String findNextNodeId(ProcessDefinition definition, String completedTaskId) {
        if (completedTaskId == null) {
            return definition.getNodes().stream()
                    .filter(n -> "userTask".equals(n.getType()))
                    .findFirst()
                    .map(Node::getId)
                    .orElse(null);
        }

        Node currentNode = definition.getNode(completedTaskId);
        if (currentNode != null) {
            return currentNode.getNext();
        }

        return null;
    }

    @Transactional
    public void rejectTask(String taskId, String rejectToNodeId,
                           Map<String, Object> variables, String comment, String actorUserId,
                           boolean terminateProcess) {
        TaskInstance currentTask = taskManager.findById(taskId);
        if (currentTask == null) {
            throw new RuntimeException("任务未找到: " + taskId);
        }

        ProcessInstance instance = currentTask.getProcessInstance();
        ProcessDefinition definition = instance.getProcessDefinition();

        Node currentNode = definition.getNode(currentTask.getTaskDefinitionId());
        if (currentNode == null || !Boolean.TRUE.equals(currentNode.getCanReject())) {
            throw new RuntimeException("当前任务不能被驳回");
        }

        if (!isValidRejectTarget(definition, currentNode, rejectToNodeId, terminateProcess)) {
            throw new RuntimeException("目标节点无效: " + rejectToNodeId);
        }

        taskManager.cancelTask(taskId, comment, actorUserId);

        if (terminateProcess) {
            // 终止流程
            instance.setStatus(com.aiti.lulian.workflow.ProcessStatus.REJECTED);
            instance.setEndTime(new Date());
            processInstanceManager.update(instance);

            // 记录驳回终止历史
            taskHistoryService.recordRejectHistory(
                    instance.getId(),
                    currentTask.getTaskDefinitionId(),
                    currentTask.getId(),
                    actorUserId,
                    comment,
                    null, // 没有驳回到的节点
                    variables
            );

            // 取消所有未完成的任务
            cancelAllPendingTasks(instance.getId(), "流程已驳回终止", actorUserId);
        }else{

            taskHistoryService.recordRejectHistory(
                    instance.getId(),
                    currentTask.getTaskDefinitionId(),
                    currentTask.getId(),
                    actorUserId,
                    comment,
                    rejectToNodeId,
                    variables
            );

            instance.setCurrentNodeId(rejectToNodeId);
            processInstanceManager.update(instance);

            Node targetNode = definition.getNode(rejectToNodeId);
            List<TaskInstance> taskList = taskManager.findByTaskDefinitionIdAndInstanceId(rejectToNodeId, instance.getId());
            String assigneeId = taskList.get(0).getAssigneeId();
            //String assigneeId = resolveAssignee(targetNode, variables, actorUserId);

            TaskInstance rejectTask = taskManager.createTask(instance, targetNode, assigneeId, variables);
            rejectTask.setType(TaskType.REJECT);
            rejectTask.setRejectToNodeId(rejectToNodeId);
            taskManager.update(rejectTask);

        }


    }

    private void cancelAllPendingTasks(String processInstanceId, String reason, String actorUserId) {
        List<TaskInstance> pendingTasks = taskManager.findByProcessInstanceIdAndStatus(
                processInstanceId, TaskStatus.PENDING);

        for (TaskInstance task : pendingTasks) {
            taskManager.cancelTask(task.getId(), reason, actorUserId);
        }
    }

    private boolean isValidRejectTarget(ProcessDefinition definition, Node currentNode, String targetNodeId, boolean terminateProcess) {
        if (definition.getNode(targetNodeId) == null) {
            if(terminateProcess){
                return true;
            }
            return false;
        }

        List<RejectRule> rules = currentNode.getRejectRules();
        if (rules == null || rules.isEmpty()) {
            return false;
        }

        for (RejectRule rule : rules) {
            if ("SPECIFIED".equals(rule.getType()) && targetNodeId.equals(rule.getNodeId())) {
                return true;
            } else if ("PREVIOUS".equals(rule.getType())) {
                // 查找上一个节点
                String previousNodeId = findPreviousNodeId(definition, currentNode.getId());
                if (targetNodeId.equals(previousNodeId)) {
                    return true;
                }
            } else if ("START".equals(rule.getType())) {
                // 查找开始节点（第一个用户任务）
                String startNodeId = definition.getNodes().stream()
                        .filter(n -> "userTask".equals(n.getType()))
                        .findFirst()
                        .map(Node::getId)
                        .orElse(null);

                if (targetNodeId.equals(startNodeId)) {
                    return true;
                }
            }
        }

        return false;
    }

    private String findPreviousNodeId(ProcessDefinition definition, String currentNodeId) {
        List<Node> nodes = definition.getNodes();
        for (int i = 0; i < nodes.size(); i++) {
            if (nodes.get(i).getId().equals(currentNodeId) && i > 0) {
                return nodes.get(i - 1).getId();
            }
        }
        return null;
    }

    public ProcessStatus getProcessStatus(String processInstanceId) {
        ProcessInstance processInstance = processInstanceManager.findById(processInstanceId);
        if (processInstance == null) {
            throw new RuntimeException("流程实例未找到: " + processInstanceId);
        }


        ProcessDefinition definition = processInstance.getProcessDefinition();

        ProcessStatus status = new ProcessStatus();
        status.setInstance(processInstance);

        List<TaskInstance> allTasks = taskManager.findByProcessInstanceId(processInstanceId);
        status.setAllTasks(allTasks);

        List<TaskInstance> currentTasks = allTasks.stream()
                .filter(t -> t.getStatus() == TaskStatus.PENDING)
                .collect(Collectors.toList());
        status.setCurrentTasks(currentTasks);

        List<TaskInstance> completedTasks = allTasks.stream()
                .filter(t -> t.getStatus() == TaskStatus.COMPLETED)
                .collect(Collectors.toList());
        status.setCompletedTasks(completedTasks);


        //List<TaskHistory> history = taskHistoryService.findByProcessInstanceId(processInstanceId);
        //status.setHistory(history);


        status.setProgress(buildProcessProgress(definition, processInstance, allTasks));

        return status;
    }

    private ProcessProgress buildProcessProgress(ProcessDefinition definition,
                                                 ProcessInstance instance,
                                                 List<TaskInstance> tasks) {
        ProcessProgress progress = new ProcessProgress();
        progress.setProcessInstanceId(instance.getId());
        progress.setCurrentNodeId(instance.getCurrentNodeId());


        List<Node> nodes = definition.getNodes().stream()
                .filter(n -> !"endEvent".equals(n.getType()))
                .collect(Collectors.toList());


        List<NodeStatus> nodeStatuses = new ArrayList<>();
        for (Node node : nodes) {
            NodeStatus nodeStatus = new NodeStatus();
            nodeStatus.setNodeId(node.getId());
            nodeStatus.setNodeName(node.getName());
            nodeStatus.setNodeType(node.getType());


            List<TaskInstance> nodeTasks = tasks.stream()
                    .filter(t -> t.getTaskDefinitionId().equals(node.getId()))
                    .collect(Collectors.toList());


            if (nodeTasks.isEmpty()) {
                nodeStatus.setStatus("PENDING");
            } else {

                TaskInstance latestTask = nodeTasks.stream()
                        .max(Comparator.comparing(TaskInstance::getStartTime))
                        .orElse(null);

                if (latestTask != null) {
                    if (latestTask.getStatus() == TaskStatus.PENDING) {
                        nodeStatus.setStatus("ACTIVE");
                        nodeStatus.setCurrentTaskId(latestTask.getId());
                        nodeStatus.setAssigneeId(latestTask.getAssigneeId());
                        nodeStatus.setAssigneeType(latestTask.getAssigneeType().name());
                    } else if (latestTask.getStatus() == TaskStatus.COMPLETED) {
                        nodeStatus.setStatus("COMPLETED");
                    } else {
                        nodeStatus.setStatus("CANCELLED");
                    }
                }
            }

            nodeStatuses.add(nodeStatus);
        }

        progress.setNodeStatuses(nodeStatuses);
        return progress;
    }

    public List<RejectOption> getRejectOptions(String taskId) {
        TaskInstance taskInstance = taskManager.findById(taskId);
        if (taskInstance == null) {
            throw new RuntimeException("任务未找到: " + taskId);
        }


        ProcessDefinition definition = taskInstance.getProcessInstance().getProcessDefinition();
        Node currentNode = definition.getNode(taskInstance.getTaskDefinitionId());

        List<RejectRule> rules = currentNode.getRejectRules();
        if (rules == null || rules.isEmpty()) {
            return Collections.emptyList();
        }

        Map<String, Object> variables = taskInstance.getVariables();
        List<RejectOption> options = new ArrayList<>();

        for (RejectRule rule : rules) {

            if (evaluateCondition(rule.getCondition(), variables)) {
                RejectOption option = new RejectOption();
                option.setRejectType(rule.getType());

                switch (rule.getType()) {
                    case "PREVIOUS":
                        String prevNodeId = findPreviousNodeId(definition, currentNode.getId());
                        Node prevNode = definition.getNode(prevNodeId);
                        option.setNodeId(prevNodeId);
                        option.setNodeName(prevNode.getName());
                        break;
                    case "SPECIFIED":
                        Node specNode = definition.getNode(rule.getNodeId());
                        option.setNodeId(rule.getNodeId());
                        option.setNodeName(specNode.getName());
                        break;
                    case "START":
                        Node startNode = definition.getNodes().stream()
                                .filter(n -> "userTask".equals(n.getType()))
                                .findFirst()
                                .orElse(null);
                        if (startNode != null) {
                            option.setNodeId(startNode.getId());
                            option.setNodeName(startNode.getName());
                        }
                        break;
                }

                option.setCondition(rule.getCondition());
                options.add(option);
            }
        }

        return options;
    }

    private boolean evaluateCondition(String condition, Map<String, Object> variables) {
        if (condition == null || condition.trim().isEmpty()) {
            return true;
        }

        try {

            if (condition.contains("#{")) {

                String expression = condition.substring(condition.indexOf("#{") + 2, condition.indexOf("}"));


                if (expression.contains("==")) {
                    String[] parts = expression.split("==");
                    String key = parts[0].trim();
                    String value = parts[1].trim().replace("'", "").replace("\"", "");

                    Object variableValue = variables.get(key);
                    return value.equals(String.valueOf(variableValue));
                }
            }

            return true;
        } catch (Exception e) {
            return false;
        }
    }


    public static class ProcessStatus {
        private ProcessInstance instance;
        private List<TaskInstance> allTasks;
        private List<TaskInstance> currentTasks;
        private List<TaskInstance> completedTasks;
        private List<TaskHistory> history;
        private ProcessProgress progress;


        public ProcessInstance getInstance() { return instance; }
        public void setInstance(ProcessInstance instance) { this.instance = instance; }

        public List<TaskInstance> getAllTasks() { return allTasks; }
        public void setAllTasks(List<TaskInstance> allTasks) { this.allTasks = allTasks; }

        public List<TaskInstance> getCurrentTasks() { return currentTasks; }
        public void setCurrentTasks(List<TaskInstance> currentTasks) { this.currentTasks = currentTasks; }

        public List<TaskInstance> getCompletedTasks() { return completedTasks; }
        public void setCompletedTasks(List<TaskInstance> completedTasks) { this.completedTasks = completedTasks; }

        public List<TaskHistory> getHistory() { return history; }
        public void setHistory(List<TaskHistory> history) { this.history = history; }

        public ProcessProgress getProgress() { return progress; }
        public void setProgress(ProcessProgress progress) { this.progress = progress; }
    }

    public static class ProcessProgress {
        private String processInstanceId;
        private String currentNodeId;
        private List<NodeStatus> nodeStatuses;


        public String getProcessInstanceId() { return processInstanceId; }
        public void setProcessInstanceId(String processInstanceId) { this.processInstanceId = processInstanceId; }

        public String getCurrentNodeId() { return currentNodeId; }
        public void setCurrentNodeId(String currentNodeId) { this.currentNodeId = currentNodeId; }

        public List<NodeStatus> getNodeStatuses() { return nodeStatuses; }
        public void setNodeStatuses(List<NodeStatus> nodeStatuses) { this.nodeStatuses = nodeStatuses; }
    }

    public static class NodeStatus {
        private String nodeId;
        private String nodeName;
        private String nodeType;
        private String status;
        private String currentTaskId;
        private String assigneeType;
        private String assigneeId;


        public String getNodeId() { return nodeId; }
        public void setNodeId(String nodeId) { this.nodeId = nodeId; }

        public String getNodeName() { return nodeName; }
        public void setNodeName(String nodeName) { this.nodeName = nodeName; }

        public String getNodeType() { return nodeType; }
        public void setNodeType(String nodeType) { this.nodeType = nodeType; }

        public String getStatus() { return status; }
        public void setStatus(String status) { this.status = status; }

        public String getCurrentTaskId() { return currentTaskId; }
        public void setCurrentTaskId(String currentTaskId) { this.currentTaskId = currentTaskId; }

        public String getAssigneeType() { return assigneeType; }
        public void setAssigneeType(String assigneeType) { this.assigneeType = assigneeType; }

        public String getAssigneeId() { return assigneeId; }
        public void setAssigneeId(String assigneeId) { this.assigneeId = assigneeId; }
    }

    public static class RejectOption {
        private String nodeId;
        private String nodeName;
        private String rejectType;
        private String condition;


        public String getNodeId() { return nodeId; }
        public void setNodeId(String nodeId) { this.nodeId = nodeId; }

        public String getNodeName() { return nodeName; }
        public void setNodeName(String nodeName) { this.nodeName = nodeName; }

        public String getRejectType() { return rejectType; }
        public void setRejectType(String rejectType) { this.rejectType = rejectType; }

        public String getCondition() { return condition; }
        public void setCondition(String condition) { this.condition = condition; }
    }
}
